package org.abstractmeta.commandify.core.command.reflectify.map;

import org.abstractmeta.commandify.core.command.AbstractReflectify;
import org.abstractmeta.reflectify.*;

import java.lang.reflect.Type;
import java.util.*;

/**
 * Represents MapReflectify
 * <p>
 * </p>
 *
 * @author Adrian Witas
 */
public class MapReflectify extends AbstractReflectify<Map> {


    public MapReflectify() {
        super(Map.class);
    }

    @Override
    protected Map<String, Accessor> getAccessors() {
        return Collections.emptyMap();
    }

    @Override
    protected Map<String, Mutator> getMutators() {
        return Collections.emptyMap();
    }

    @Override
    public Provider<Map> getProvider(Class... argumentTypes) {
        return new Provider<Map>() {

            @Override
            public <T> ParameterSetter<T> getParameterSetter(Class<T> argumentType, int argumentIndex) {
                throw new UnsupportedOperationException();
            }

            @Override
            public ParameterSetter<Object> getParameterSetter(int argumentIndex) {
                throw new UnsupportedOperationException();
            }

            @Override
            public Type[] getGenericParameterTypes() {
                return new Type[0];
            }

            @Override
            public Class[] getParameterTypes() {
                return new Class[0];
            }

            @Override
            public Map get() {
                return new HashMap();
            }
        };
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Provider<Map>> getProviders() {
        return Arrays.<Provider<Map>>asList(getProvider());
    }


    @Override
    public <T> MethodInvoker<Map, T> getMethodInvoker(Class<T> methodResultType, String methodName, Class... argumentTypes) {
        if("get".equals(methodName)) {
            return new GetMethodInvoker<T>();
        } else if("put".equals(methodName)) {
            return new PutMethodInvoker<T>();
        }
        throw new UnsupportedOperationException("Failed to lookup method:" + methodName);
    }

    @Override
    public MethodInvoker<Map, Object> getMethodInvoker(String methodName, Class... argumentTypes) {
        return getMethodInvoker(Object.class, methodName, argumentTypes);
    }

    @Override
    public List<MethodInvoker<Map, Object>> getMethodInvokers(String methodName) {
        throw new UnsupportedOperationException();
    }


    public static class PutMethodInvoker<T> implements MethodInvoker<Map, T> {

        private Object key;
        private Object value;


        @Override
        public <T1> ParameterSetter<T1> getParameterSetter(Class<T1> argumentType, int argumentIndex) {
            final PutMethodInvoker self = this;
            if (argumentIndex == 0) {
                return new ParameterSetter<T1>() {

                    @Override
                    public void set(T1 value) {
                        self.key = value;

                    }
                };
            }
            if (argumentIndex == 1) {
                return new ParameterSetter<T1>() {

                    @Override
                    public void set(T1 value) {
                        self.value= value;

                    }
                };
            }
            throw new IndexOutOfBoundsException("Invalid index parameter");
        }

        @Override
        public ParameterSetter<Object> getParameterSetter(int argumentIndex) {
            return getParameterSetter(Object.class, argumentIndex);
        }

        @Override
        @SuppressWarnings("unchecked")
        public T invoke(Map instance) {
            return (T)instance.put(key, value);
        }

        @Override
        public Type[] getGenericParameterTypes() {
            return new Type[0];
        }

        @Override
        public Class[] getParameterTypes() {
            return new Class[0];
        }

        @Override
        public Type getResultGenericType() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Class getResultType() {
            throw new UnsupportedOperationException();
        }
    }



    public static class GetMethodInvoker<T> implements MethodInvoker<Map, T> {

        private Object key;


        @Override
        public <T1> ParameterSetter<T1> getParameterSetter(Class<T1> argumentType, int argumentIndex) {
            final GetMethodInvoker self = this;
            if (argumentIndex == 0) {
                return new ParameterSetter<T1>() {

                    @Override
                    public void set(T1 value) {
                        self.key = value;

                    }
                };
            }
            throw new IndexOutOfBoundsException("Invalid index parameter");
        }

        @Override
        public ParameterSetter<Object> getParameterSetter(int argumentIndex) {
            return getParameterSetter(Object.class, argumentIndex);
        }

        @Override
        @SuppressWarnings("unchecked")
        public T invoke(Map instance) {
            return (T)instance.get(key);
        }

        @Override
        public Type[] getGenericParameterTypes() {
            return new Type[0];
        }

        @Override
        public Class[] getParameterTypes() {
            return new Class[0];
        }

        @Override
        public Type getResultGenericType() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Class getResultType() {
            throw new UnsupportedOperationException();
        }
    }


}
